/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <gtkmm/label.h>
#include <ETL/stringf>
#include <gtkmm/celleditable.h>
#include <gtkmm/editable.h>
#include <gtkmm/entry.h>
#include <gtkmm/eventbox.h>
#include <gtk/gtkentry.h> /* see XXX below */

#include "app.h"
#include "widget_value.h"
#include "widget_vector.h"
#include "widget_filename.h"
#include "widget_enum.h"
#include "widget_color.h"
#include "widget_canvaschooser.h"
#include "widget_time.h"

#include "cellrenderer_gradient.h"
#include "cellrenderer_value.h"

#include "widget_gradient.h"
#include "dialog_gradient.h"
#include "dialog_color.h"
#include <gtkmm/textview.h>

#include "general.h"

#endif

using namespace synfig;
using namespace etl;
using namespace std;
using namespace studio;

/* === M A C R O S ========================================================= */

#define DIGITS		15

/* === G L O B A L S ======================================================= */

class studio::ValueBase_Entry : public Gtk::EventBox, public Gtk::CellEditable
{
	Glib::ustring path;
	Widget_ValueBase *valuewidget;
	bool edit_done_called;
	Gtk::Widget *parent;
public:
	ValueBase_Entry():
		Glib::ObjectBase  (typeid(ValueBase_Entry)),
		Gtk::EventBox     (),
		Gtk::CellEditable ()
	{
		parent=0;
		edit_done_called=false;
/*
		  Gtk::HBox *const hbox = new Gtk::HBox(false, 0);
		  add(*Gtk::manage(hbox));

		  Gtk::Entry *entry_ = new Gtk::Entry();
			entry_->set_text("bleh");
		  hbox->pack_start(*Gtk::manage(entry_), Gtk::PACK_EXPAND_WIDGET);
		  entry_->set_has_frame(false);
		  entry_->gobj()->is_cell_renderer = true; // XXX

*/
		valuewidget=manage(new class Widget_ValueBase());
		valuewidget->inside_cellrenderer();
		add(*valuewidget);
		valuewidget->show();

		//set_flags(Gtk::CAN_FOCUS);
		//set_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);

		/*
		set_events(//(Gdk::ALL_EVENTS_MASK)
		~(	Gdk::EXPOSURE_MASK
			| Gdk::ENTER_NOTIFY_MASK
			| Gdk::LEAVE_NOTIFY_MASK
			| Gdk::FOCUS_CHANGE_MASK
			| Gdk::STRUCTURE_MASK
			| Gdk::PROPERTY_CHANGE_MASK
			| Gdk::VISIBILITY_NOTIFY_MASK
			| Gdk::PROXIMITY_IN_MASK
			| Gdk::PROXIMITY_OUT_MASK
			| Gdk::SUBSTRUCTURE_MASK
		)
		);
		*/
		//signal_editing_done().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));
		//signal_remove_widget().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));

		show_all_children();

		//signal_show().connect(sigc::mem_fun(*this, &ValueBase_Entry::grab_focus));
	}
	~ValueBase_Entry()
	{
	}

	void on_editing_done()
	{
		hide();
		if(parent)parent->grab_focus();
		if(!edit_done_called)
		{
			edit_done_called=true;
			Gtk::CellEditable::on_editing_done();
		}
		else
		{
			synfig::error("on_editing_done(): Called twice!");
		}
	}
	void set_parent(Gtk::Widget*x) { parent=x; }
	void on_remove_widget()
	{
		hide();
		edit_done_called=true;
		if(parent)parent->grab_focus();
		Gtk::CellEditable::on_remove_widget();
	}
	void start_editing_vfunc(GdkEvent */*event*/)
	{
		valuewidget->signal_activate().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::editing_done));
		show();
		//valuewidget->grab_focus();
		//get_window()->set_focus(*valuewidget);
	}
	bool on_event(GdkEvent *event)
	{
		if(event->any.type==GDK_BUTTON_PRESS ||
			event->any.type==GDK_2BUTTON_PRESS ||
			event->any.type==GDK_KEY_PRESS ||
			event->any.type==GDK_KEY_RELEASE ||
			event->any.type==GDK_SCROLL ||
			event->any.type==GDK_3BUTTON_PRESS)
			return true;
		return Gtk::EventBox::on_event(event);
	}
	void on_grab_focus()
	{
		Gtk::EventBox::on_grab_focus();
		if(valuewidget)
			valuewidget->grab_focus();
	}
	void set_path(const Glib::ustring &p)
	{
		path=p;
	}
	void set_value(const synfig::ValueBase &data)
	{
		if(valuewidget)
			valuewidget->set_value(data);
		//valuewidget->grab_focus();
	}
	void set_canvas(const etl::handle<synfig::Canvas> &data)
	{
		assert(data);
		if(valuewidget)
			valuewidget->set_canvas(data);
	}
	void set_param_desc(const synfig::ParamDesc &data)
	{
		if(valuewidget)
			valuewidget->set_param_desc(data);
	}

	const synfig::ValueBase &get_value()
	{
		if(valuewidget)
			return valuewidget->get_value();

		warning("%s:%d this code shouldn't be reached", __FILE__, __LINE__);
		return *(new synfig::ValueBase());
	}

	const Glib::ustring &get_path()
	{
		return path;
	}

};

/* === P R O C E D U R E S ================================================= */

bool get_paragraph(synfig::String& text)
{
	Gtk::Dialog dialog(
		_("Paragraph"),		// Title
		true,		// Modal
		true		// use_separator
	);
	Gtk::Label label(_("Enter Paragraph Text Here:"));
	label.show();
	dialog.get_vbox()->pack_start(label);


	Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
	text_buffer->set_text(text);

	Gtk::TextView text_view(text_buffer);
	text_view.show();
	dialog.get_vbox()->pack_start(text_view);

/*
	Gtk::Entry entry;
	entry.set_text(text);
	entry.show();
	entry.set_activates_default(true);
	dialog.get_vbox()->pack_start(entry);
*/

	dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
	dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
	dialog.set_default_response(Gtk::RESPONSE_OK);

	//text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));

	dialog.show();

	if(dialog.run()!=Gtk::RESPONSE_OK)
		return false;

	text=text_buffer->get_text();

	return true;
}

/* === M E T H O D S ======================================================= */

CellRenderer_ValueBase::CellRenderer_ValueBase():
	Glib::ObjectBase	(typeid(CellRenderer_ValueBase)),
	Gtk::CellRendererText	(),
	property_value_	(*this,"value",synfig::ValueBase()),
	property_canvas_(*this,"canvas",etl::handle<synfig::Canvas>()),
	property_param_desc_(*this,"param_desc",synfig::ParamDesc())
{
	CellRendererText::signal_edited().connect(sigc::mem_fun(*this,&CellRenderer_ValueBase::string_edited_));
	value_entry=new ValueBase_Entry();
	value_entry->hide();

	Pango::AttrList attr_list;
	{
		Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*8));
		pango_size.set_start_index(0);
		pango_size.set_end_index(64);
		attr_list.change(pango_size);
	}
	property_attributes()=attr_list;

	property_foreground()=Glib::ustring("#7f7f7f");
	property_inconsistent()=false;
}

CellRenderer_ValueBase::~CellRenderer_ValueBase()
{
	if (getenv("SYNFIG_DEBUG_DESTRUCTORS"))
		synfig::info("CellRenderer_ValueBase::~CellRenderer_ValueBase(): Deleted");
}

void
CellRenderer_ValueBase::string_edited_(const Glib::ustring&path,const Glib::ustring&str)
{
	ValueBase old_value=property_value_.get_value();
	ValueBase value;

	if(old_value.get_type()==ValueBase::TYPE_TIME)
	{
		value=ValueBase(Time((String)str,get_canvas()->rend_desc().get_frame_rate()));
	}
	else
		value=ValueBase((String)str);

	if(old_value!=value)
		signal_edited_(path,value);
}

void
CellRenderer_ValueBase::render_vfunc(
		const Glib::RefPtr<Gdk::Drawable>& window,
		Gtk::Widget& widget,
		const Gdk::Rectangle& background_area,
		const Gdk::Rectangle& ca,
		const Gdk::Rectangle& expose_area,
		Gtk::CellRendererState flags)
{
	if(!window)
		return;
//	const unsigned int cell_xpad = property_xpad();
//	const unsigned int cell_ypad = property_ypad();

	//int x_offset = 0, y_offset = 0;
//	int	width = ca.get_width();
	int	height = ca.get_height();
//	get_size(widget, ca, x_offset, y_offset, width, height);

//	width  -= cell_xpad * 2;
//	height -= cell_ypad * 2;

//	if(width <= 0 || height <= 0)
//		return;

	Gtk::StateType state = Gtk::STATE_INSENSITIVE;
	if(property_editable())
		state = Gtk::STATE_NORMAL;
	if((flags & Gtk::CELL_RENDERER_SELECTED) != 0)
		state = (widget.has_focus()) ? Gtk::STATE_SELECTED : Gtk::STATE_ACTIVE;

	ValueBase data=property_value_.get_value();

	switch(data.get_type())
	{
	case ValueBase::TYPE_REAL:
		if(((synfig::ParamDesc)property_param_desc_).get_is_distance())
		{
			Distance x(data.get(Real()),Distance::SYSTEM_UNITS);
			x.convert(App::distance_system,get_canvas()->rend_desc());
			property_text()=(Glib::ustring)x.get_string(6).c_str();
		}
		else
			property_text()=(Glib::ustring)strprintf("%.6f",data.get(Real()));
		break;
	case ValueBase::TYPE_TIME:
		property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format());
		break;
	case ValueBase::TYPE_ANGLE:
		property_text()=(Glib::ustring)strprintf("%.2f DEG",(Real)Angle::deg(data.get(Angle())).get());
		break;
	case ValueBase::TYPE_INTEGER:
		if(((synfig::ParamDesc)property_param_desc_).get_hint()!="enum")
		{
			property_text()=(Glib::ustring)strprintf("%i",data.get(int()));
		}
		else
		{
			property_text()=(Glib::ustring)strprintf("(%i)",data.get(int()));
			std::list<synfig::ParamDesc::EnumData> enum_list=((synfig::ParamDesc)property_param_desc_).get_enum_list();
			std::list<synfig::ParamDesc::EnumData>::iterator iter;

			for(iter=enum_list.begin();iter!=enum_list.end();iter++)
				if(iter->value==data.get(int()))
				{
					// don't show the key_board s_hortcut under_scores
					String local_name = iter->local_name;
					String::size_type pos = local_name.find_first_of('_');
					if (pos != String::npos)
						property_text() = local_name.substr(0,pos) + local_name.substr(pos+1);
					else
						property_text() = local_name;
					break;
				}
		}

		break;
	case ValueBase::TYPE_VECTOR:
		{
			Vector vector=data.get(Vector());
			Distance x(vector[0],Distance::SYSTEM_UNITS),y(vector[1],Distance::SYSTEM_UNITS);
			x.convert(App::distance_system,get_canvas()->rend_desc());
			y.convert(App::distance_system,get_canvas()->rend_desc());
			property_text()=static_cast<Glib::ustring>(strprintf("%s,%s",x.get_string(6).c_str(),y.get_string(6).c_str()));
		}
		break;

	case ValueBase::TYPE_STRING:

		if(data.get_type()==ValueBase::TYPE_STRING)
		{
			if(!data.get(synfig::String()).empty())
				property_text()=static_cast<Glib::ustring>(data.get(synfig::String()));
			else
				property_text()=Glib::ustring("<empty>");
		}
		break;
	case ValueBase::TYPE_CANVAS:
		if(data.get(etl::handle<synfig::Canvas>()))
		{
			if(data.get(etl::handle<synfig::Canvas>())->is_inline())
				property_text()=_("<Inline Canvas>");
			else
				property_text()=(Glib::ustring)data.get(etl::handle<synfig::Canvas>())->get_id();
		}
		else
			property_text()="<No Image Selected>";
		break;
	case ValueBase::TYPE_COLOR:
		{
			render_color_to_window(window,ca,data.get(Color()));
			return;
		}
		break;
	case ValueBase::TYPE_BOOL:
		{
			widget.get_style()->paint_check(
				Glib::RefPtr<Gdk::Window>::cast_static(window), state,
				data.get(bool())?Gtk::SHADOW_IN:Gtk::SHADOW_OUT,
				ca, widget, "cellcheck",
				ca.get_x()/* + x_offset + cell_xpad*/,
				ca.get_y()/* + y_offset + cell_ypad*/,
				height-1,height-1);
			return;
		}
		break;
	case ValueBase::TYPE_NIL:
		//property_text()=(Glib::ustring)" ";
		return;
		break;
	case ValueBase::TYPE_SEGMENT:
		property_text()=(Glib::ustring)_("Segment");
		break;
	case ValueBase::TYPE_GRADIENT:
		render_gradient_to_window(window,ca,data.get(Gradient()));
		return;
		break;
	case ValueBase::TYPE_LIST:
		property_text()=(Glib::ustring)_("List");
		break;
	case ValueBase::TYPE_BLINEPOINT:
		property_text()=(Glib::ustring)_("BLine Point");
		break;
	default:
		property_text()=static_cast<Glib::ustring>(_("UNKNOWN"));
		break;
	}
	CellRendererText::render_vfunc(window,widget,background_area,ca,expose_area,flags);
}


/*
bool
CellRenderer_ValueBase::activate_vfunc(	GdkEvent* event,
	Gtk::Widget& widget,
	const Glib::ustring& path,
	const Gdk::Rectangle& background_area,
	const Gdk::Rectangle& cell_area,
	Gtk::CellRendererState flags)
{
	ValueBase data=(ValueBase)property_value_.get_value();

	switch(data.type)
	{
	case ValueBase::TYPE_BOOL:
		if(property_editable())
			signal_edited_(path,ValueBase(!data.get(bool())));
    	return true;
	case ValueBase::TYPE_STRING:
		return CellRendererText::activate_vfunc(event,widget,path,background_area,cell_area,flags);
	}
	return false;
}
*/

void
CellRenderer_ValueBase::gradient_edited(synfig::Gradient gradient, Glib::ustring path)
{
	ValueBase old_value(property_value_.get_value());
	ValueBase value(gradient);
	if(old_value!=value)
		signal_edited_(path,value);
}

void
CellRenderer_ValueBase::color_edited(synfig::Color color, Glib::ustring path)
{
	ValueBase old_value(property_value_.get_value());
	ValueBase value(color);
	if(old_value!=value)
		signal_edited_(path,value);
}

Gtk::CellEditable*
CellRenderer_ValueBase::start_editing_vfunc(
	GdkEvent* event __attribute__ ((unused)),
	Gtk::Widget& widget,
	const Glib::ustring& path,
	const Gdk::Rectangle& background_area __attribute__ ((unused)),
	const Gdk::Rectangle& cell_area __attribute__ ((unused)),
	Gtk::CellRendererState flags __attribute__ ((unused)))
{
	// If we aren't editable, then there is nothing to do
	if(!property_editable())
		return 0;

	ValueBase data=property_value_.get_value();

	switch(data.get_type())
	{
	case ValueBase::TYPE_BOOL:
		signal_edited_(path,ValueBase(!data.get(bool())));
    	return NULL;
	//case ValueBase::TYPE_TIME:
	//	property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format()|Time::FORMAT_FULL);
	//	return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);

	case ValueBase::TYPE_GRADIENT:
		App::dialog_gradient->reset();
		App::dialog_gradient->set_gradient(data.get(Gradient()));
		App::dialog_gradient->signal_edited().connect(
			sigc::bind(
				sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::gradient_edited),
				path
			)
		);
		App::dialog_gradient->present();

		return NULL;

	case ValueBase::TYPE_COLOR:
		App::dialog_color->reset();
		App::dialog_color->set_color(data.get(Color()));
		App::dialog_color->signal_edited().connect(
			sigc::bind(
				sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::color_edited),
				path
			)
		);
		App::dialog_color->present();

		return NULL;
	case ValueBase::TYPE_STRING:
		if(get_param_desc().get_hint()=="paragraph")
		{
			synfig::String string;
			string=data.get(string);
			if(get_paragraph(string))
				signal_edited_(path,ValueBase(string));
			return NULL;
		}
		// if(get_param_desc().get_hint()!="filename")
			// return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);
	default:
		{
			assert(get_canvas());
			//delete value_entry;
			value_entry=manage(new ValueBase_Entry());
			value_entry->set_path(path);
			value_entry->set_canvas(get_canvas());
			value_entry->set_param_desc(get_param_desc());
			value_entry->set_value(data);
			value_entry->set_parent(&widget);
			value_entry->signal_editing_done().connect(sigc::mem_fun(*this, &CellRenderer_ValueBase::on_value_editing_done));
			return value_entry;
		}
	}
	return NULL;
}

void
CellRenderer_ValueBase::on_value_editing_done()
{
	if(value_entry)
	{
		ValueBase old_value(property_value_.get_value());
		ValueBase value(value_entry->get_value());

		if(old_value!=value)
			signal_edited_(value_entry->get_path(),value);

		//delete value_entry;
		//value_entry=0;
	}
}
